package com.example.hsh.homefragment.view;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcelable;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.OverScroller;

import com.example.hsh.homefragment.R;

import static android.R.attr.y;
import static android.os.Build.VERSION_CODES.M;


public class StickyNavLayout extends LinearLayout implements NestedScrollingParent,NestedScrollingChild{
    private static final String TAG = "StickyNavLayout";
    private float mTotalUnconsumed;
    private NestedScrollingParentHelper mNestedScrollingParentHelper;
    private NestedScrollingChildHelper mNestedScrollingChildHelper;
    private final int[] mParentScrollConsumed = new int[2];
    private final int[] mParentOffsetInWindow = new int[2];
    private ValueAnimator animator;

    //不在使用remainHeight保留高度，而是插多一个布局位topNavHeight
    private View topNav;
    private int topNavHeight;

    private boolean doingAnimate;

    /**
     * 判定为拖动的最小移动像素数
     */
    private int mTouchSlop;

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        Log.e(TAG, "onStartNestedScroll");
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        Log.e(TAG, "onNestedScrollAccepted");
        // Reset the counter of how much leftover scroll needs to be consumed.
        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
        // Dispatch up to the nested parent
        boolean scroll = startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);

        Log.d(TAG, "onNestedScrollAccepted: scroll=" + scroll);

        mTotalUnconsumed = 0;
    }

    @Override
    public void onStopNestedScroll(View target) {
        Log.e(TAG, "onStopNestedScroll");
        mNestedScrollingParentHelper.onStopNestedScroll(target);
        // Finish the spinner for nested scrolling if we ever consumed any
        // unconsumed nested scroll
        if (mTotalUnconsumed > 0) {
            mTotalUnconsumed = 0;
        }
        // Dispatch up our nested parent
        stopNestedScroll();
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
//        Log.e(TAG, "onNestedScroll");

        //child已经不能在向下滑动，判断当前还需要消耗dy，不需要再交给parent
        if (dyUnconsumed < 0 && getScrollY() <= (expand ? 0 : topNavHeight)/*remainHeight*/ && !ViewCompat.canScrollVertically(target, -1)) {
            dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                    mParentOffsetInWindow);
            Log.d(TAG, "onNestedScroll: dyUnconsumed=" + dyUnconsumed);
        }


        //向上滑，当前无需消耗dy，先交给parent，确保先隐藏加载，在隐藏顶部栏
        if (dyConsumed > 0 && getScrollY() >= 0) {
            Log.d(TAG, "onNestedScroll: dyConsumed="+dyConsumed);

            dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                    mParentOffsetInWindow);
        }

//        Log.d(TAG, "onNestedScroll: dispatchNestedScroll=" + dispatchNestedScroll);
//        Log.d(TAG, "onNestedScroll: mParentOffsetInWindow[1]=" + mParentOffsetInWindow[1]);

        // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are
        // sometimes between two nested scrolling views, we need a way to be able to know when any
        // nested scrolling parent has stopped handling events. We do that by using the
        // 'offset in window 'functionality to see if we have been moved from the event.
        // This is a decent indication of whether we should take over the event stream or not.
        final int dy = dyUnconsumed + mParentOffsetInWindow[1];
        if (dy < 0) {
            mTotalUnconsumed += Math.abs(dy);
        }
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        Log.e(TAG, "onNestedPreScroll");

        //返回时应该先让parent先消耗
        // Now let our nested parent consume the leftovers
        final int[] parentConsumed = mParentScrollConsumed;
        if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
            Log.d(TAG, "onNestedPreScroll: mParentScrollConsumed=" + parentConsumed[1]);
            consumed[1] = dy;
            dx -= parentConsumed[0];
            dy -= parentConsumed[1];
        }


        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight + topNavHeight;
        boolean showTop = dy < 0 && getScrollY() > (expand ? 0 : topNavHeight)/*remainHeight*/ && !ViewCompat.canScrollVertically(target, -1);
        Log.d(TAG, "onNestedPreScroll: dy=" + dy);
        if (hiddenTop || showTop) {
            scrollBy(0, dy);
            consumed[1] = dy;

            Log.d(TAG, "onNestedPreScroll: consumed[1]=" + consumed[1]);
            Log.d(TAG, "onNestedPreScroll: mTotalUnconsumed=" + mTotalUnconsumed);
        }

        // If we are in the middle of consuming, a scroll, then we want to move the spinner back up
        // before allowing the list to scroll
//        if (dy > 0 && mTotalUnconsumed > 0) {
//            if (dy > mTotalUnconsumed) {
//                consumed[1] = dy - (int) mTotalUnconsumed;
//                mTotalUnconsumed = 0;
//            } else {
//                mTotalUnconsumed -= dy;
//                consumed[1] = dy;
//            }
//        }

    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        Log.e(TAG, "onNestedFling");
        return dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        Log.e(TAG, "onNestedPreFling");
        //down - //up+
        if (getScrollY() >= mTopViewHeight + topNavHeight) return false;

        if (getScrollY() <= (expand ? 0 : topNavHeight)/*remainHeight*/) {
            return false;
        }

        fling((int) velocityY);
        return dispatchNestedPreFling(velocityX, velocityY);
    }

    @Override
    public int getNestedScrollAxes() {
        Log.e(TAG, "getNestedScrollAxes");
        return mNestedScrollingParentHelper.getNestedScrollAxes();
    }

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;

    private OverScroller mScroller;

    private int remainHeight;

    private int remainHeaderHeight;

    private boolean expand = false;

    private boolean closeWhenScrollUp;

    public StickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        setOrientation(LinearLayout.VERTICAL);
        mScroller = new OverScroller(context);

        // 获取TouchSlop值
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StickyNavLayout);
        int indexCount = typedArray.getIndexCount();
        for (int i=0;i<indexCount;i++) {
            int index = typedArray.getIndex(i);
            switch (index) {
                case R.styleable.StickyNavLayout_remainHeight:
                    remainHeaderHeight = typedArray.getDimensionPixelSize(index, 0);
                    break;

                case R.styleable.StickyNavLayout_closeWhenScrollUp:
                    closeWhenScrollUp = typedArray.getBoolean(index, true);
                    break;
            }
        }
        typedArray.recycle();
        setNestedScrollingEnabled(true);

        remainHeight = remainHeaderHeight;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.top_panel);
        mNav = findViewById(R.id.tl_tabs);
        topNav = findViewById(R.id.top_nav);
        View view = findViewById(R.id.vp_main);
        if (!(view instanceof ViewPager)) {
            throw new RuntimeException(
                    "id_stickynavlayout_viewpager show used by ViewPager !");
        }
        mViewPager = (ViewPager) view;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        scrollTo(0, topNavHeight);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //不限制顶部的高度
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY));
        measureChild(mTop,widthMeasureSpec,heightMeasureSpec);
        //设置ViewPager的高度，
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();

        setMeasuredDimension(getMeasuredWidth(), topNav.getMeasuredHeight() + mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mViewPager.getMeasuredHeight());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
        Log.d(TAG, "onSizeChanged: mTopViewHeight=" + mTopViewHeight);

        topNavHeight = topNav.getMeasuredHeight();

        Log.d(TAG, "onSizeChanged: topNavHeight=" + topNavHeight);
    }


    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, (expand ? 0 : topNavHeight), mTopViewHeight + topNavHeight/* - remainHeight*/);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight + topNavHeight) {
            y = mTopViewHeight + topNavHeight;
        }

        //没有展开的情况下
        if (!doingAnimate && !expand && y < topNavHeight/*remainHeight*/) {
            y = topNavHeight/*remainHeight*/;
        }

        //展开的情况下
        if (expand && y < 0) {
            y = 0;
        }

        //当为展开的情况下，向上滑，是否要关闭
        if (closeWhenScrollUp && !doingAnimate && expand && y >= topNavHeight) {
            expand = false;
        }

        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }
    }

    public void openRemainHeader(boolean anim){
        remainHeight = 0;
        if (!anim) {
            scrollTo(0, 0);
        }else{
            animateRemainHeader(getScrollY(), 0);
        }
    }

    public void closeRemainHeader(boolean anim){
        remainHeight = remainHeaderHeight;
        if (!anim) {
            scrollTo(0, topNavHeight/*remainHeaderHeight*/);
        }else{
            animateRemainHeader(getScrollY(), topNavHeight/*remainHeaderHeight*/);
        }
    }

    public void toggleRemainHeader(boolean anim) {
        int scrollY = getScrollY();
        if (!expand) {
            expand = true;
            openRemainHeader(anim);
        }else{
            expand = false;
            closeRemainHeader(anim);
        }
    }

    private void animateRemainHeader(int start, int end) {
        Log.d(TAG, "animateRemainHeader: start=" + start + ",end=" + end);

        if (animator != null && animator.isRunning()) {
            return;
        }

        if (animator == null) {
            animator = new ValueAnimator();
            animator.setInterpolator(new DecelerateInterpolator());
            animator.addUpdateListener(animation -> {
                doingAnimate = true;
                int currentY = (int) animation.getAnimatedValue();
                Log.d(TAG, "animateRemainHeader: currentY=" + currentY);
                Log.d(TAG, "animateRemainHeader: getScrollY()=" + getScrollY());
                scrollBy(0, currentY - getScrollY());

                if (animation.getAnimatedFraction() == 1) {
                    doingAnimate = false;
                }
            });
            /*animator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    *//*expand = !expand*//*;
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });*/
        }else{
            animator.cancel();
        }
        animator.setDuration(600);
        animator.setIntValues(start, end);
        animator.start();
    }

    @Override
    public void computeScroll() {
        //实现平滑滚动的逻辑
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        return super.onSaveInstanceState();
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        super.onRestoreInstanceState(state);

    }

    public void setNestedScrollingEnabled(boolean enabled) {
        mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
    }

    public boolean isNestedScrollingEnabled() {
        return mNestedScrollingChildHelper.isNestedScrollingEnabled();
    }
    public boolean startNestedScroll(int axes) {
        return mNestedScrollingChildHelper.startNestedScroll(axes);
    }
    public void stopNestedScroll() {
        mNestedScrollingChildHelper.stopNestedScroll();
    }
    public boolean hasNestedScrollingParent() {
        return mNestedScrollingChildHelper.hasNestedScrollingParent();
    }
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                                        int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
                dxUnconsumed, dyUnconsumed, offsetInWindow);
    }
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

    //====================================================================================

    private float mLastMoveY;
    private float currentMoveY;

    /**
     * Used during scrolling to retrieve the new offset within the window.
     */
    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mLastMoveY = currentMoveY = ev.getY();
        } else if (ev.getAction() == MotionEvent.ACTION_MOVE&&inScrollableRange(ev.getY())) {
            float scrollDis = ev.getY() - mLastMoveY;
            if (Math.abs(scrollDis) > mTouchSlop) {
                return true;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;

            case MotionEvent.ACTION_MOVE:
                int deltaY = (int) (mLastMoveY - mLastMoveY);
                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    deltaY -= mScrollConsumed[1];
                }

                break;

            case MotionEvent.ACTION_UP:

                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 在StickyNavLayout的滚动范围内
     * @param y
     * @return
     */
    private boolean inScrollableRange(float y) {
        return y >= 0 && y <= mTopViewHeight + topNavHeight;
    }
}